import log from '../../dxmodules/dxLogger.js'
import config from '../../dxmodules/dxConfig.js'
import driver from '../driver.js'
import codeService from '../service/codeService.js'
import configService from '../service/configService.js'
import common from '../../dxmodules/dxCommon.js'
import dxMap from '../../dxmodules/dxMap.js'
import std from '../../dxmodules/dxStd.js'
import utils from '../common/utils/utils.js'
import base64 from '../../dxmodules/dxBase64.js'
import net from '../../dxmodules/dxNet.js'
import * as os from "os"
import ota from "../../dxmodules/dxOta.js";
import bus from "../../dxmodules/dxEventBus.js"
import dxstd from '../../dxmodules/dxStd.js'
import safeService from '../service/safeService.js'
const vgProService = {}

vgProService.receiveMsg = function (data) {
    // log.info('[vgProService] receiveMsg:' + JSON.stringify(data))
    if (!data.bcc) {
        log.error("Checksum error")
        return
    }
    // The pass-through mode and protocol mode are independent, but the pass-through mode allows the execution of individual protocol commands, such as configuring tool connections and performing upgrade operations.
    const specProCmds = ["37", "b0", "54", "58", "56", "5a", "57", "81", "82", "83", "07", "21", "45", "0b"]
    if ((config.get('sysInfo.w_mode') == 2 && config.get("sysInfo.dchannel") == 64) || specProCmds.includes(data.cmd)) {
        let topic = "cmd" + data.cmd
        if (!this[topic]) {
            log.error("This method has not been implemented.", topic)
            return
            // return driver.uart485.sendVg({ "cmd": data.cmd, "result": '90', "length": 0, "data": '' })
        }
        this[topic]({ source: '485', pack: data })
    }
}

//Device status query
vgProService.cmd01 = function (data) {
    log.info("{vgPro} [cmd01] req:" + JSON.stringify(data))
    send(data.source, { "cmd": "01", "length": 2, "result": "00", "data": "55AA" })
}

//Get Device ID
vgProService.cmd02 = function (data) {
    log.info("{vgPro} [cmd02] req:" + JSON.stringify(data))
    let devnum = config.get('sysInfo.devnum')
    if (!devnum) {
        send(data.source, { "cmd": "02", "length": 4, "result": "00", "data": "00000000" })
        return
    }
    let data1 = common.decimalToLittleEndianHex(devnum, 4)
    send(data.source, { "cmd": "02", "length": data1.length / 2, "result": "00", "data": data1 })
}

//Update or query the scanner system time
vgProService.cmd03 = function (data) {
    log.info("{vgPro} [cmd03] req:" + JSON.stringify(data))
    let pack = data.pack
    if (pack.length == 0) {
        let data1 = utils.timestampToHexLE(new Date().getTime(), 16)
        send(data.source, { "cmd": "03", "length": data1.length / 2, "result": "00", "data": data1 })
        return
    }
    let data1 = pack.data.substring(0, 2)
    if (data1 == '00') {
        //Automatic time update is not working on the DW200 device.
        common.asyncReboot(2)
        send(data.source, { "cmd": "03", "length": 0, "result": "00", "data": "" })
    } else if (data1 == '01') {
        //The DW200 device is not responding to the scheduled time update.
        send(data.source, { "cmd": "03", "length": 0, "result": "00", "data": "" })
    } else if (data1 == '02') {
        //Setting the time might cause the watchdog to restart.
        let map = dxMap.get('workerId')
        let time = common.littleEndianToDecimal(pack.data.substring(2))
        common.systemBrief('date  "' + utils.formatUnixTimestamp(time) + '"')
        driver.watchdog.feed("controller", 30)
        driver.watchdog.feed("main", 30)
        let workerIds = map.get('workerId')
        workerIds.forEach(element => {
            driver.watchdog.feed(element, 30)
        });
        send(data.source, { "cmd": "03", "length": 0, "result": "00", "data": "" })
    }
}

//Buzzer and LED control
vgProService.cmd04 = function (data) {
    log.info("{vgPro} [cmd04] req:" + JSON.stringify(data))
    let pack = data.pack
    pack.data = common.hexStringToUint8Array(pack.data)
    if ((pack.data[0] >> 3 & 1) === 1) {
        //Control buzzer
        send(data.source, { "cmd": "04", "length": 0, "result": "00", "data": "" })
        driver.pwm.beep(pack.data[2] * 50, pack.data[3] * 50, pack.data[1])
        return
    }
    send(data.source, { "cmd": "04", "length": 0, "result": "00", "data": "" })
}

//On/off barcode scanning function
vgProService.cmd05 = function (data) {
    log.info("{vgPro} [cmd05] req:" + JSON.stringify(data))
    let pack = data.pack
    if (pack.data == '00') {
        // Start
        configService.configVerifyAndSave({ "sysInfo": { 'codeSwitch': 1 } })
    } else if (pack.data == '01') {
        configService.configVerifyAndSave({ "sysInfo": { 'codeSwitch': 0 } })
    } else {
        send(data.source, { "cmd": '05', "result": '90', "length": 0, 'data': '' })
        return
    }
    send(data.source, { "cmd": "05", "length": 0, "result": "00", "data": "" })
}

//The on/off key value is not reported by ID Info 4000.
vgProService.cmd06 = function (data) {
    log.info("{vgPro} [cmd06] req:" + JSON.stringify(data) + 'functionality not working')
    send(data.source, { "cmd": "06", "length": 0, "result": "00", "data": "" })
}


//Random number generation security module usage
vgProService.cmd07 = function (data) {
    log.info("{vgPro} [cmd07] req:" + JSON.stringify(data))
    driver.sync.response("getRandom", data.pack.data)
}


//Door opening/closing magnetic field query
vgProService.cmd09 = function (data) {
    log.info("{vgPro} [cmd09] req:" + JSON.stringify(data))
    let sensorData = dxMap.get("SENSOR").get("data")
    if (sensorData && sensorData.code == 48) {
        if (sensorData.value) {
            send(data.source, { "cmd": "09", "length": 1, "result": "00", "data": "00" })
        } else {
            send(data.source, { "cmd": "09", "length": 1, "result": "00", "data": "01" })
        }
    } else {
        send(data.source, { "cmd": "09", "length": 0, "result": "0e", "data": "" })
    }
}

//Obtain device SN
vgProService.cmd0a = function (data) {
    log.info("{vgPro} [cmd0a] req:" + JSON.stringify(data))
    let pack = data.pack
    // Get device SN
    if (pack.length > 0) {
        let newSn = common.hexToString(pack.data)
        //Modify the sn number to be an input parameter.
        try {
            let wgetApp = common.systemWithRes(`test -e "/etc/.sn" && echo "OK" || echo "NO"`, 2)
            if (!wgetApp.includes('OK')) {
                //Did not create file
                common.systemBrief("touch /etc/.sn")
            }
            std.saveFile('/etc/.sn', newSn)
        } catch (error) {
            log.info('Reason for failure to write sn to 0A: ', error.stack)
            send(data.source, { "cmd": '0A', "result": '90', "length": 0, 'data': '' })
            return
        }
        //Return to serial port
        send(data.source, { "cmd": '0A', "result": '00', length: common.stringToHex(newSn).length / 2, 'data': common.stringToHex(newSn) })
        common.asyncReboot(2)
    } else {
        log.info('-----0A query-----', common.getSn());
        send(data.source, { "cmd": '0A', "result": '00', length: common.stringToHex(common.getSn()).length / 2, "data": common.stringToHex(common.getSn()) })
    }
}

//If getting the device UUID dw200 returns no results, I'll write it the same as getting the SN.
vgProService.cmd0b = function (data) {
    log.info("{vgPro} [cmd0B] req:" + JSON.stringify(data))
    if (config.get('sysInfo.safe_open') === 1) {
        log.info("Security module modifies key return value")
        driver.sync.response("updateKey", data.pack.result)

        return;
    }
    send(data.source, { "cmd": '0b', "result": '00', length: common.getSn().length / 2, "data": common.getSn() })
}

//Get the main controller chipID
vgProService.cmd0c = function (data) {
    log.info("{vgPro} [cmd0C] req:" + JSON.stringify(data))
    send(data.source, { "cmd": '0c', "result": '00', length: common.stringToHex(common.getUuid()).length / 2, "data": common.stringToHex(common.getUuid()) })
}

//Get Mac
vgProService.cmd10 = function (data) {
    log.info("{vgPro} [cmd10] req:" + JSON.stringify(data))
    send(data.source, { "cmd": '10', "result": '00', length: common.stringToHex(net.getMacaddr(net.TYPE.ETHERNET)).length / 2, "data": common.stringToHex(net.getMacaddr(net.TYPE.ETHERNET)) })
}

//QR, barcode, NFC settings
vgProService.cmd21 = function (data) {
    log.info("{vgPro} [cmd21] req:" + JSON.stringify(data));
    if (config.get('sysInfo.safe_open') === 1) {
        //Safety module door opening return value
        log.info("Safety module door opening return value");
        driver.sync.response("openDoor", data.pack.result)
        return;
    }
    let pack = data.pack;
    // Mapping relationship between control words and code systems
    //            Bit7	    Bit6	Bit5	Bit4	Bit3	Bit2        Bit1	  Bit0
    // control word 1:  CODE39	ISBN13	EAN13	EAN8	NFC     Barcode Activation      DM        QR
    // Coding system:      bit4     bit3    bit2    bit1    \           \        \          bit0
    // Control word 2   UPCE   ISBN10   ITF    PDF417    BAR_EXP  DATABAR   CODE128     CODE93
    // Coding system:      bit13   bit12   bit11    bit9      bit8     bit7      bit6       bit5
    let byte_map1 = [0, null, null, null, 1, 2, 3, 4]
    let byte_map2 = [5, 6, 7, 8, 9, 11, 12, 13]

    let firstByte, secondByte
    if (pack.data.length >= 2) {
        // control word 1
        firstByte = parseInt(pack.data.substring(0, 2), 16)
    }
    if (pack.data.length >= 4) {
        // control word 2
        secondByte = parseInt(pack.data.substring(2, 4), 16)
    }
    let de_type = pack.data == '00' ? 0 : config.get('sysInfo.de_type')

    for (let i = 0; i < 8; i++) {
        // Check if the corresponding bit of the control word is 1
        if (firstByte && ((1 << i) & firstByte)) {
            if (typeof byte_map1[i] == 'number') {
                // Add the mapped value to the configuration
                de_type |= 1 << byte_map1[i]
            } else {
                // if (i != 3 && i != 2 && i != 1) {
                //     send(data.source, { "cmd": '21', "result": '90', 'length': 0, 'data': '' });
                // }
                log.error("Code system that cannot be modified, bit:", i)
            }
        }
        // The same applies to control word 2.
        if (secondByte && ((1 << i) & secondByte)) {
            if (typeof byte_map2[i] == 'number') {
                de_type |= 1 << byte_map2[i]
            } else {
                log.error("Code system that cannot be modified, bit:", i)
            }
        }
    }

    let dataInfo = { "sysInfo": { "de_type": de_type } }
    if ((firstByte >> 3) & 1 == 1) {
        dataInfo.sysInfo.nfc = 1
    }
    if ((firstByte >> 2) & 1 == 1) {
        dataInfo.sysInfo.de_type = 64510
    }
    console.log('-------',JSON.stringify(dataInfo));
    
    configService.configVerifyAndSave(dataInfo)
    common.asyncReboot(2)
    send(data.source, { "cmd": '21', "result": '00', 'length': 0, 'data': '' });
}

// QR code scanning working mode settings   
vgProService.cmd22 = function (data) {
    log.info("{vgPro} [cmd22] req:" + JSON.stringify(data));
    let pack = data.pack;
    // In the configuration file, 0 represents an interval and 1 represents a single occurrence.
    if (pack.length == 1) {
        if (pack.data == "02") {
            // Single mode
            config.setAndSave('sysInfo.s_mode', 1)
        } else if (pack.data == "01") {
            // Normal mode
            config.setAndSave('sysInfo.s_mode', 2)
        } else {
            return send(data.source, { "cmd": '22', "result": '90', "length": 0, 'data': '' });
        }
    } else {
        // Interval mode
        if (pack.length != 3 || pack.data.substring(0, 2) != "03") {
            return send(data.source, { "cmd": '22', "result": '90', "length": 0, 'data': '' })
        }
        config.setAndSave('sysInfo.s_mode', 0)
        try {
            let interval = common.littleEndianToDecimal(pack.data.substring(2))
            config.setAndSave('sysInfo.interval', interval * 1000)
        } catch (error) {
            send(data.source, { "cmd": '22', "result": '90', "length": 0, 'data': '' })
            return
        }
    }
    send(data.source, { "cmd": '22', "result": '00', "length": 0, 'data': '' });
}

// Scan interval setting in interval mode
vgProService.cmd23 = function (data) {
    log.info("{vgPro} [cmd23] req:" + JSON.stringify(data));
    if (config.get('sysInfo.safe_open') === 1) {
        // The security module reports the door sensor status.
        log.info("Security module door magnetic sensor status reporting")
        let sensorData = dxMap.get("SENSOR").get("data") // Parse configuration code
        if (sensorData && sensorData.code == 48) {
            if (sensorData.value) {
                send(data.source, { "cmd": "09", "length": 1, "result": "00", "data": "01" })
            } else {
                send(data.source, { "cmd": "09", "length": 1, "result": "00", "data": "00" })
            }
        } else {
            send(data.source, { "cmd": "09", "length": 1, "result": "00", "data": "01" })
        }
        return;
    }
    let pack = data.pack;
    let interval = common.littleEndianToDecimal(pack.data)
    if (interval > 6000) {
        send(data.source, { "cmd": '23', "result": '90', "length": 0, 'data': '' })
        return
    }
    config.setAndSave('sysInfo.interval', interval)
    send(data.source, { "cmd": '23', "result": '00', "length": 0, 'data': '' });
}

//Backlight control: None
vgProService.cmd24 = function (data) {
    log.info("{vgPro} [cmd24] req:" + JSON.stringify(data));
    send(data.source, { "cmd": '24', "result": '00', "length": 0, 'data': '' });
}

//Buzzer Response Configuration
vgProService.cmd25 = function (data) {
    log.info("{vgPro} [cmd25] req:" + JSON.stringify(data));
    let pack = data.pack
    let ascan = config.get('sysInfo.ascan')
    let anfc = config.get('sysInfo.anfc')
    if (pack.data == '01') {
        //The buzzer sounds after scanning the code or swiping the card.
        config.setAndSave('sysInfo.ascan', ascan |= 1 << 0)
        config.setAndSave('sysInfo.anfc', anfc |= 1 << 0)
    } else if (pack.data == '00') {
        //The buzzer does not activate after scanning the code or swiping the card.
        config.setAndSave('sysInfo.ascan', ascan &= ~(1 << 0))
        config.setAndSave('sysInfo.anfc', anfc &= ~(1 << 0))
    }
    send(data.source, { "cmd": '25', "result": '00', "length": 0, 'data': '' });
}

//GPIO_0 controls devices that are only supported by default (MX86).
vgProService.cmd26 = function (data) {
    log.info("{vgPro} [cmd26] req:" + JSON.stringify(data));
    send(data.source, { "cmd": '26', "result": '00', "length": 0, 'data': '' });
}

//GPIO_1 controls devices that are only supported by default (MX86).
vgProService.cmd27 = function (data) {
    log.info("{vgPro} [cmd27] req:" + JSON.stringify(data));
    send(data.source, { "cmd": '27', "result": '00', "length": 0, 'data': '' });
}

//GPIO_0 and GPIO_1 output high-level voltage control; by default, only (MX86) devices are supported.
vgProService.cmd28 = function (data) {
    log.info("{vgPro} [cmd28] req:" + JSON.stringify(data));
    send(data.source, { "cmd": '28', "result": '00', "length": 0, 'data': '' });
}

//Audio control
vgProService.cmd29 = function (data) {
    log.info("{vgPro} [cmd29] req:" + JSON.stringify(data))
    let pack = data.pack
    let index = parseInt(pack.data, 16)
    let res = common.systemWithRes(`test -e "/app/code/resource/wav/${index}.wav" && echo "OK" || echo "NO"`, 2)
    if (!res.includes('OK')) {
        send(data.source, { "cmd": '63', "result": '90', "length": 0, 'data': '' })
        return
    }
    send(data.source, { "cmd": '29', "result": '00', "length": 0, 'data': '' });
    driver.audio.play(index)

}

//Relay control
vgProService.cmd2a = function (data) {
    log.info("{vgPro} [cmd2a] req:" + JSON.stringify(data))
    let pack = data.pack
    pack.data = common.hexStringToUint8Array(pack.data)

    if (pack.data[0] != 1 && pack.data[0] != 0) {
        send(data.source, { "cmd": '2A', "result": '90', "length": 0, 'data': '' });
        return;
    }

    if (config.get("sysInfo.safe_open") === 1) {
        if (pack.data[0] == "01") {
            if (pack.length == 1) {
                //Normally open
                safeService.open();
            } else {
                //Safety module door opening
                safeService.openDoorByTime(pack.data[1] * 50);
            }
        }

    } else {
        if (pack.data[0] == 0) {
            //Turn off relay
            driver.gpio.relay(0)
        } else if (pack.data[0] == 1) {
            if (pack.length == 1) {
                //Normal open
                driver.gpio.always()
            } else {
                //Open for a specified duration
                driver.gpio.relay(1, pack.data[1] * 50)
            }
        }
    }

    send(data.source, { "cmd": '2A', "result": '00', "length": 0, 'data': '' });
}
/**
 * Heartbeat enable and heartbeat reporting
 */
vgProService.cmd2b = function (data) {
    log.info("{vgPro} [cmd2b] req:" + JSON.stringify(data))
    let pack = data.pack
    //Heartbeat reporting settings
    let dataJson = JSON.parse(common.hexToString(pack.data));
    if (dataJson.heart_en == undefined) {
        send(data.source, { "cmd": '2b', "result": '90', "length": 0, 'data': '' })
        return
    }
    let sysInfo = { "heart_en": dataJson.heart_en, "heart_time": dataJson.heart_time, "heart_data": dataJson.heart_data }
    if (!dataJson.heart_time) {
        delete sysInfo.heart_time;
    }
    if (!dataJson.heart_data) {
        delete sysInfo.heart_data;
    }
    if (data.source == '485') {
        bus.fire('heart485', sysInfo)
        send(data.source, { "cmd": '2b', "result": '00', "length": 0, 'data': '' });
    } else {
        let res = configService.configVerifyAndSave({ "sysInfo": sysInfo })
        if (res == true) {
            send(data.source, { "cmd": '2b', "result": '00', "length": 0, 'data': '' });

        } else {
            send(data.source, { "cmd": '2b', "result": '90', "length": 0, 'data': '' })

        }
    }
}
// Query version number
vgProService.cmd37 = function (data) {
    log.info("{vgPro} [cmd37] req:" + JSON.stringify(data))
    let version = common.stringToHex(config.get('sysInfo.version'))
    send(data.source, { "cmd": '37', "result": '00', length: version.length / 2, 'data': version })
}

//Result reporting mode settings
vgProService.cmd31 = function (data) {
    log.info("{vgPro} [cmd31] req:" + JSON.stringify(data))
    //0x00 The host computer polls at 0x30 to obtain data from the barcode scanner.
    //0x01 This indicates that the 0x30 instruction is selected to actively report data.
    //0x80 This indicates that the host computer is polling 0x33 to obtain scanner data.
    //0x81 This indicates that the 0x33 instruction is selected to actively report data.
    let pack = data.pack
    pack.data = common.hexStringToUint8Array(pack.data)
    //check
    if (pack.length != 1 && pack.length != 2) {
        //Length error, return an error directly.
        send(data.source, { "cmd": '31', "result": '90', "length": 0, 'data': '' })
        return
    }
    //The first byte of the content
    let type = pack.data[0].toString(16)
    if (type != '0' && type != '1' && type != '80' && type != '81') {
        //Type error returns an error directly.
        send(data.source, { "cmd": '31', "result": '90', "length": 0, 'data': '' })
        return
    }
    config.setAndSave('sysInfo.report_mode', type)
    if (pack.length == 2) {
        //The validity period of the second byte of data (in 50ms) is 0x00 -> 50ms; non-zero -> N*50ms.
        let time = pack.data[1]
        config.setAndSave('sysInfo.report_timeout', time == 0 ? 50 : time * 50)
    } else {
        config.setAndSave('sysInfo.report_timeout', 0)
    }
    send(data.source, { "cmd": '31', "result": '00', "length": 0, 'data': '' })

}

//Result 1 (regardless of data source)
vgProService.cmd30 = function (data) {
    log.info("{vgPro} [cmd30] req:" + JSON.stringify(data))
    let map = dxMap.get("VGPRO")
    let codeReport = map.get('codeReport')
    if (config.get('sysInfo.report_mode') == '0' && codeReport) {
        //The reporting mode is 00. The host computer polls 0x30 to obtain scanner data before returning data. Otherwise, the default response is null.
        let reportTimeout = config.get('sysInfo.report_timeout') || 2000
        if (reportTimeout == 0 || reportTimeout == undefined || new Date().getTime() - codeReport.time <= reportTimeout) {
            // Data valid
            let data1 = codeReport.data
            send(data.source, { "cmd": "30", "length": data1.length / 2, "result": "00", "data": data1 })
            return
        } else {
            send(data.source, { "cmd": '30', "result": '00', "length": 0, 'data': '' })
            map.del('codeReport')
        }
    } else {
        send(data.source, { "cmd": '30', "result": '00', "length": 0, 'data': '' })
    }
}

//Result 2 (Distinction)
vgProService.cmd33 = function (data) {
    log.info("{vgPro} [cmd33] req:" + JSON.stringify(data))
    let map = dxMap.get("VGPRO")
    let codeReport = map.get('codeReport')
    if (config.get('sysInfo.report_mode') == '80' && codeReport) {
        //The reporting mode is that the host computer polls 0x33 to obtain scanner data before returning data; otherwise, the default response is null.
        let reportTimeout = config.get('sysInfo.report_timeout') || 2000
        if (reportTimeout == 0 || reportTimeout == undefined || new Date().getTime() - codeReport.time <= reportTimeout) {
            // Data valid
            let data1 = codeReport.sourceMark + codeReport.data
            send(data.source, { "cmd": "33", "length": data1.length / 2, "result": "00", "data": data1 })
            return
        } else {
            send(data.source, { "cmd": '33', "result": '00', "length": 0, 'data': '' })
            map.del('codeReport')
        }
    } else {
        send(data.source, { "cmd": '33', "result": '00', "length": 0, 'data': '' })
    }
}

// Key value reporting is not available on ID Info 4000.
vgProService.cmd32 = function (data) {
    log.info("{vgPro} [cmd32] req:" + JSON.stringify(data))
    send(data.source, { "cmd": '32', "result": '00', "length": 0, 'data': '' })
}

/**
* Safety module configuration relay action time recovery
* @param {*} data 
*/
vgProService.cmd45 = function (data) {
    log.info("{vgPro} [cmd45] req:" + JSON.stringify(data));
    driver.sync.response("updateRelayd", data.pack.result)
}


// Card number reporting switch
vgProService.cmd53 = function (data) {
    log.info("{vgPro} [cmd53] req:" + JSON.stringify(data))
    let pack = data.pack
    if (pack.data == '00') {
        //Exit command mode

    } else if (pack.data == '01') {
        //Enter command mode

    } else if (pack.data == '02') {
        //NFC reporting
        config.setAndSave('sysInfo.nfcReport', 1)
    } else if (pack.data == '03') {
        //Close reporting
        config.setAndSave('sysInfo.nfcReport', 0)
    } else {
        send(data.source, { "cmd": '53', "result": '90', "length": 0, 'data': '' })
        return
    }
    send(data.source, { "cmd": '53', "result": '00', "length": 0, 'data': '' })
}


//Read a block of data from the M1 card
vgProService.cmd51 = function (data) {
    try {
        log.info("{vgPro} [cmd51] req:" + JSON.stringify(data))
        let pack = data.pack
        if (pack.length < 8) {
            send(data.source, { "cmd": '51', "result": '90', "length": 0, 'data': '' })
        }
        pack.data = common.hexStringToUint8Array(pack.data)
        let keyType = pack.data[0]
        if (keyType != 96 && keyType != 97) {
            send(data.source, { "cmd": '51', "result": '90', "length": 0, 'data': '' })
            return
        }

        let blkNum = pack.data[1]
        let key = pack.data.slice(2, 8)
        let hexKey = common.arrToHex(Array.from(key))
        if (keyType == 96 && hexKey != "ffffffffffff") {
            send(data.source, { "cmd": '51', "result": '90', "length": 0, 'data': '' })
            return
        }
        if (blkNum > 59) {
            send(data.source, { "cmd": '51', "result": '90', "length": 0, 'data': '' })
            return
        }

        let taskFlg = 0x00
        if (pack.length == 9) {

            taskFlg = pack.data[8]
        }
        let res = driver.nfc.m1cardReadBlk(taskFlg, blkNum, key, keyType == 96 ? 0X60 : 0X61)
        if (res) {
            let sendData = [0x55, 0xAA, 0x51, 0x00, 0x10, 0x00].concat(res)
            let bcc = common.calculateBcc(sendData)
            send(data.source, sendData.concat([bcc]).map(v => v.toString(16).padStart(2, '0')).join(''))
        } else {
            send(data.source, { "cmd": '51', "result": '90', "length": 0, 'data': '' })
        }
    } catch (error) {
        log.error("{vgPro} [cmd51] :" + error.stack)
        send(data.source, { "cmd": '51', "result": '00', "length": 0, 'data': '' })
    }
}

//Write a block of data to the M1 card
vgProService.cmd52 = function (data) {
    try {
        log.info("{vgPro} [cmd52] req:" + JSON.stringify(data))
        let pack = data.pack
        if (data.dlen < 24) {
            send(data.source, { "cmd": '52', "result": '90', "length": 0, 'data': '' })
        }
        pack.data = common.hexStringToUint8Array(pack.data)
        let keyType = pack.data[0]
        let blkNum = pack.data[1]
        let key = pack.data.slice(2, 8)
        let nfcData = pack.data.slice(8, 24)
        let taskFlg = 0x00
        if (pack.length == 25) {
            taskFlg = pack.data[24]
        }
        let res = driver.nfc.m1cardWriteBlk(taskFlg, blkNum, key, keyType, nfcData)
        if (res) {
            send(data.source, { "cmd": '52', "result": '00', "length": 0, 'data': '' })
        } else {
            send(data.source, { "cmd": '52', "result": '90', "length": 0, 'data': '' })
        }
    } catch (error) {
        log.error("{vgPro} [cmd52] :" + error.stack)
        send(data.source, { "cmd": '52', "result": '90', "length": 0, 'data': '' })
    }
}

//Read multiple blocks within the M1 card sector
vgProService.cmda0 = function (data) {
    try {
        log.info("{vgPro} [cmda0] req:" + JSON.stringify(data))
        let pack = data.pack
        if (data.dlen < 11) {
            send(data.source, { "cmd": 'A0', "result": '90', "length": 0, 'data': '' })
        }
        pack.data = common.hexStringToUint8Array(pack.data)
        let taskFlg = pack.data[0]
        let keyType = pack.data[1]
        let secNum = pack.data[2]
        let logicBlkNum = pack.data[3]
        let blkNums = pack.data[4]
        let key = pack.data.slice(5, 11)
        let res = driver.nfc.m1cardReadSector(taskFlg, secNum, logicBlkNum, blkNums, key, keyType)
        if (res) {
            let sendData = [0x55, 0xAA, 0xa0, 0x00, res.length, 0x00].concat(res)
            let bcc = common.calculateBcc(sendData)
            send(data.source, sendData.concat([bcc]).map(v => v.toString(16).padStart(2, '0')).join(''))
        } else {
            send(data.source, { "cmd": 'A0', "result": '90', "length": 0, 'data': '' })
        }
    } catch (error) {
        log.error("{vgPro} [cmda0] :" + error.stack)
        send(data.source, { "cmd": 'A0', "result": '90', "length": 0, 'data': '' })
    }
}

//Write multiple blocks within the M1 card sector
vgProService.cmda1 = function (data) {
    try {
        log.info("{vgPro} [cmda1] req:" + JSON.stringify(data))
        let pack = data.pack
        if (data.dlen < 11) {
            send(data.source, { "cmd": 'A1', "result": '90', "length": 0, 'data': '' })
        }
        pack.data = common.hexStringToUint8Array(pack.data)
        let taskFlg = pack.data[0]
        let keyType = pack.data[1]
        let secNum = pack.data[2]
        let logicBlkNum = pack.data[3]
        let blkNums = pack.data[4]
        let key = pack.data.slice(5, 11)
        let nfcData = pack.data.slice(11, 11 + blkNums * 16)
        let res = driver.nfc.m1cardWriteSector(taskFlg, secNum, logicBlkNum, blkNums, key, keyType, nfcData)
        if (res) {
            send(data.source, { "cmd": 'A1', "result": '00', "length": 0, 'data': '' })
        } else {
            send(data.source, { "cmd": 'A1', "result": '90', "length": 0, 'data': '' })
        }
    } catch (error) {
        log.error("{vgPro} [cmda1] :" + error.stack)
        send(data.source, { "cmd": 'A1', "result": '90', "length": 0, 'data': '' })
    }
}

// Send APDU command. Not currently performed.
vgProService.cmda6 = function (data) {
    log.info("{vgPro} [cmda6] req:" + JSON.stringify(data))
    send(data.source, { "cmd": 'A6', "result": '00', "length": 0, 'data': '' })
}

//Responds to QR code scanning, card swiping, and Bluetooth data.
vgProService.cmd61 = function (data) {
    log.info("{vgPro} [cmd61] req:" + JSON.stringify(data))
    let pack = data.pack
    try {
        let dataJson = JSON.parse(common.utf8HexToStr(pack.data));
        driver.screen.customShowMsgAndImg(dataJson.ack + '\n' + dataJson.msg, 2000)
    } catch (error) {
        send(data.source, { "cmd": '61', "result": '90', "length": 0, 'data': '' })
        return
    }
    send(data.source, { "cmd": '61', "result": '00', "length": 0, 'data': '' })
}

//Display custom data
vgProService.cmd62 = function (data) {
    log.info("{vgPro} [cmd62] req:" + JSON.stringify(data))
    let pack = data.pack
    let mode = pack.data.substring(0, 2)
    pack.data = pack.data.substring(2)
    let jsonData = JSON.parse(common.utf8HexToStr(pack.data))
    if (mode == "01") {
        // Display text
        if (jsonData.page_data) {
            driver.screen.customShowMsgAndImg({ center: jsonData.page_data, bottom_left: jsonData.key_left, bottom_mid: jsonData.key_mid, bottom_right: jsonData.key_right, })
        }
    } else if (mode == "02") {
        // Show QR code
        driver.screen.customShowMsgAndImg({ dynamic_qr_str: jsonData.dynamic_qr_str })
    }
    send(data.source, { "cmd": '62', "result": '00', "length": 0, 'data': '' })
}

// Show image
vgProService.cmd63 = function (data) {
    log.info("{vgPro} [cmd63] req:" + JSON.stringify(data))
    let pack = data.pack
    let index = parseInt(pack.data, 16)
    let res = common.systemWithRes(`test -e "/app/code/resource/image/${index}.png" && echo "OK" || echo "NO"`, 2)
    if (!res.includes('OK')) {
        send(data.source, { "cmd": '63', "result": '90', "length": 0, 'data': '' })
        return
    }
    driver.screen.showPic({ time: 2000, name: index })
    send(data.source, { "cmd": '63', "result": '00', "length": 0, 'data': '' })
}

//Enter a specific window
vgProService.cmd64 = function (data) {
    log.info("{vgPro} [cmd64] req:" + JSON.stringify(data))
    let pack = data.pack
    if (pack.data == "0100") {
        // driver.screen.customShowMsgAndImg(" ", 100)
        driver.screen.showSystemInfo(false)
    } else if (pack.data == "0200") {
        driver.screen.showSystemInfo(true)
    } else {
        send(data.source, { "cmd": '64', "result": '90', "length": 0, 'data': '' })
        return
    }
    send(data.source, { "cmd": '64', "result": '00', "length": 0, 'data': '' })
}

//Pop-up window displays custom data
vgProService.cmd65 = function (data) {
    log.info("{vgPro} [cmd65] req:" + JSON.stringify(data))
    let pack = data.pack
    try {
        let jsonData = JSON.parse(common.utf8HexToStr(pack.data))
        if (utils.isEmpty(jsonData.msg) || utils.isEmpty(jsonData.isCloseEnable) || utils.isEmpty(jsonData.msgTimeoutMs)) {
            send(data.source, { "cmd": '65', "result": '90', "length": 0, 'data': '' })
        } else {
            driver.screen.customPopWin(jsonData.msg, jsonData.msgTimeoutMs, jsonData.isCloseEnable, "waring", true)
            send(data.source, { "cmd": '65', "result": '00', "length": 0, 'data': '' })
        }
    } catch (error) {
        send(data.source, { "cmd": '65', "result": '90', "length": 0, 'data': '' })
    }

}

// Start transmitting data
vgProService.cmd54 = function (data) {
    log.info("{vgPro} [cmd54] req:" + JSON.stringify(data))
    //Delete the corresponding compressed file
    common.systemBrief('rm -rf /app/data/upgrades/download.zip')
    send(data.source, { "cmd": '54', "result": '00', "length": 0, 'data': '' })

    dxMap.get("VGPRO").del("updateSize")
    dxMap.get("VGPRO").del("updateProgress")
    dxMap.get("VGPRO").put("updateSize", common.littleEndianToDecimal(data.pack.data))

    if (config.get("sysInfo.language") == 1) {
        driver.screen.customPopWin("Start Upgrading", 2000, true, "waring", false)
    } else {
        driver.screen.customPopWin("Start upgrading", 2000, true, "waring", false)
    }

    // Timeout mark
    dxMap.get("VGPRO").put("updateTimeout", new Date().getTime())
    let timer = dxstd.setInterval(() => {
        let updateTimeout = dxMap.get("VGPRO").get("updateTimeout")
        if (!updateTimeout) {
            dxstd.clearInterval(timer)
            return
        }
        if (new Date().getTime() - updateTimeout > 2000) {
            if (config.get("sysInfo.language") == 1) {
                driver.screen.customPopWin("Upgrade interruption", 600000, true, "waring", false)
            } else {
                driver.screen.customPopWin("Upgrade interruption", 600000, true, "waring", false)
            }
            dxstd.clearInterval(timer)
            driver.pwm.beep(null, null, 1)
        }
    }, 2000)
}

//Transmit data
vgProService.cmd58 = function (data) {
    log.info("{vgPro} [cmd58] req:")
    // log.info("{vgPro} [cmd58] req:" + JSON.stringify(data))
    let pack = data.pack
    let fpath = "/upgrades.zip"
    let fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_APPEND);
    //Failed to open
    if (fd < 0) {
        return
    }
    let arraybuffer = common.hexStringToArrayBuffer(pack.data)
    let pngBuffer = new Uint8Array(arraybuffer);
    os.write(fd, pngBuffer.buffer, 0, pngBuffer.length)
    // Close file descriptor
    os.close(fd);
    send(data.source, { "cmd": '58', "result": '00', "length": 0, 'data': '' })

    let updateSize = dxMap.get("VGPRO").get("updateSize")
    let updateProgress = dxMap.get("VGPRO").get("updateProgress")
    if (!updateProgress) {
        updateProgress = updateSize
    }

    let lastSize = updateProgress - pack.data.length / 2
    let progress = (1 - (lastSize / updateSize)) * 100;

    dxMap.get("VGPRO").put("updateProgress", lastSize)

    if (config.get("sysInfo.language") == 1) {
        driver.screen.customPopWin("Upgrading " + Math.floor(progress) + "%", 2000, true, "waring", false)
    } else {
        driver.screen.customPopWin("Upgrading " + Math.floor(progress) + "%", 2000, true, "waring", false)
    }

    dxMap.get("VGPRO").put("updateTimeout", new Date().getTime())
}

//0x56 / 0x5A Stop transmitting data
vgProService.cmd56 = function (data) {
    log.info("{vgPro} [cmd56] req:" + JSON.stringify(data))
    send(data.source, { "cmd": '56', "result": '00', "length": 0, 'data': '' })
}
//0x56 / 0x5A Stop transmitting data
vgProService.cmd5a = function (data) {
    log.info("{vgPro} [0x5A] req:" + JSON.stringify(data))
    let pack = data.pack
    let fpath = "/app/data/upgrades/download.zip"
    let res = false
    if (std.exist(fpath) && common.md5HashFile(fpath).map(v => v.toString(16).padStart(2, '0')).join('').toLowerCase() == pack.data.toLowerCase()) {
        res = true
    }
    send(data.source, { "cmd": '5A', "result": res ? '00' : '90', "length": 0, 'data': '' })
}

vgProService.cmd57 = function (data) {
    log.info("{vgPro} [cmd57] req:" + JSON.stringify(data))
    send(data.source, { "cmd": '57', "result": '00', "length": 0, 'data': '' })

    dxMap.get("VGPRO").del("updateTimeout")

    if (config.get("sysInfo.language") == 1) {
        driver.screen.customPopWin("Upgrade Successfully", 2000, true, "waring", false)
    } else {
        driver.screen.customPopWin("Upgrade Successfully", 2000, true, "waring", false)
    }

    let fpath = "/app/data/upgrades/download.zip"
    const temp = ota.OTA_ROOT + '/temp'
    common.systemBrief(`rm -rf ${ota.OTA_ROOT} && mkdir ${ota.OTA_ROOT} `) //First delete the OTA root directory
    // Unzip
    common.systemBrief(`mkdir ${temp} && unzip -o ${fpath} -d ${temp}`)
    // Build script file
    let shell = `cp -r ${temp}/* /app/code \n rm -rf ${ota.OTA_ROOT}`

    common.systemBrief(`echo "${shell}" > ${ota.OTA_RUN} && chmod +x ${ota.OTA_RUN}`)
    let fileExist = (os.stat(ota.OTA_RUN)[1] === 0)
    if (!fileExist) {
        throw new Error('Build shell file failed')
    }
    common.systemWithRes(`${ota.OTA_RUN}`)
    common.asyncReboot(2)
}

// Configure/Query Device
vgProService.cmdb0 = function (data) {
    log.info("{vgPro} [cmdb0] req:" + JSON.stringify(data))
    let pack = data.pack
    // Query/Modify Device Configuration
    let str = pack.data
    if (!str) {
        return
    }
    //The first byte of the data field indicates whether it's a modification or a query: 00 for query, 01 for modification.
    if (parseInt(str.substring(0, 2)) == 0) {
        //Query configuration
        let data1 = common.strToUtf8Hex(utils.jsonObjectToString(config.getAll()))
        // let data1 = common.strToUtf8Hex(utils.jsonObjectToString({ beepd: 500, taddr: "192.168.62.201",show_unlocking:1 }))
        send(data.source, { "cmd": 'B0', "result": '00', "length": data1.length / 2, "data": data1 })
    } else {
        //Modify configuration
        if (pack.dlen <= 1) {
            return
        }
        // ___VBAR_CONFIG_V1.1.0___{ble_name="11127S"}--lLqHBRnE2bU8D2HJ5RTioQ==
        let toString = common.utf8HexToStr(str.substring(2))
        if (!checkConfigCode(toString)) {
            driver.pwm.fail()
            log.error("Configuration code verification failed.")
            return
        }
        let content = parseString(toString)
        if (content.sn) {
            //Modify the sn number to be an input parameter.
            try {
                let wgetApp = common.systemWithRes(`test -e "/etc/.sn" && echo "OK" || echo "NO"`, 2)
                if (!wgetApp.includes('OK')) {
                    //Did not create
                    common.systemBrief("touch /etc/.sn")
                }
                std.saveFile('/etc/.sn', content.sn)
            } catch (error) {
                log.info('Writing to the /etc/.sn file failed. Reason: ', error.stack)
                send(data.source, { "cmd": 'B0', "result": '90', "length": 0, "data": '' })
                return
            }
            common.asyncReboot(2)
        }

        //Verify
        let res = configService.configVerifyAndSave({ 'sysInfo': content })
        if (typeof res != 'boolean') {
            log.error(res)
            send(data.source, { "cmd": 'B0', "result": '90', "length": 0, "data": '' })
            driver.pwm.fail()
            return
        }
        if (content.ble_name) {
            let config = driver.uartBle.getConfig()
            if (config.name != content.ble_name) {
                res = false
                log.error('Bluetooth modification failed')
            }
        }
        if (res) {
            driver.pwm.success()
        } else {
            driver.pwm.fail()
            log.error("Configuration failed")
            send(data.source, { "cmd": 'B0', "result": '90', "length": 0, "data": '' })
            return
        }
        if (content.reboot === 1) {
            common.asyncReboot(1)
        }
        send(data.source, { "cmd": 'B0', "result": '00', "length": 0, "data": '' })
    }
}


//Audio or image update preparation
vgProService.cmd81 = function (data) {
    log.info("{vgPro} [cmd81] req:" + JSON.stringify(data))
    let pack = data.pack
    //Delete directory
    common.systemBrief(`rm -rf ${ota.OTA_ROOT} && mkdir ${ota.OTA_ROOT} `)
    //Record the size of the data to be received
    let map = dxMap.get("VGPRO")
    let count = common.littleEndianToDecimal(pack.data.substring(2, 6))
    let size = utils.hexLEToTimestamp(pack.data.substring(6))
    map.put('upgradeInfo', { count: count, size: size })
    send(data.source, { "cmd": '81', "result": '00', "length": 0, 'data': '' })
    if (config.get("sysInfo.language") == 1) {
        driver.screen.customPopWin("Start Upgrading", 2000, true, "waring", false)
    } else {
        driver.screen.customPopWin("Start Upgrading", 2000, true, "waring", false)
    }

    // Timeout mark
    dxMap.get("VGPRO").put("updateTimeout", new Date().getTime())
    let timer = dxstd.setInterval(() => {
        let updateTimeout = dxMap.get("VGPRO").get("updateTimeout")
        if (!updateTimeout) {
            dxstd.clearInterval(timer)
            return
        }
        if (new Date().getTime() - updateTimeout > 2000) {
            if (config.get("sysInfo.language") == 1) {
                driver.screen.customPopWin("Upgrade interruption", 600000, true, "waring", false)
            } else {
                driver.screen.customPopWin("Upgrade interruption", 600000, true, "waring", false)
            }
            dxstd.clearInterval(timer)
            driver.pwm.beep(null, null, 1)
        }
    }, 2000)

}

//Audio or image update packet transmission
vgProService.cmd82 = function (data) {
    // log.info("{vgPro} [cmd82] req:" + JSON.stringify(data))
    let pack = data.pack
    let packNum = common.littleEndianToDecimal(pack.data.substring(0, 4))
    let map = dxMap.get("VGPRO")
    let upgradeInfo = map.get('upgradeInfo')
    if (!upgradeInfo || !upgradeInfo.count || !upgradeInfo.size) {
        send(data.source, { "cmd": '82', "result": '90', "length": 0, 'data': '' })
        return
    }
    let firmware = ota.OTA_ROOT + '/download.zip'
    let fd = os.open(firmware, os.O_RDWR | os.O_CREAT | os.O_APPEND);
    //Failed to open
    if (fd < 0) {
        return
    }
    let arraybuffer = common.hexStringToArrayBuffer(pack.data.substring(4))
    let pngBuffer = new Uint8Array(arraybuffer);
    os.write(fd, pngBuffer.buffer, 0, pngBuffer.length)
    os.close(fd)
    send(data.source, { "cmd": '82', "result": '00', "length": 0, 'data': '' })
    let index = utils.hexLEToTimestamp(pack.data.substring(0, 4))
    if (config.get("sysInfo.language") == 1) {
        driver.screen.customPopWin("Upgrading " + Math.floor((index / upgradeInfo.count) * 100) + "%", 2000, true, "waring", false)
    } else {
        driver.screen.customPopWin("Upgrading " + Math.floor((index / upgradeInfo.count) * 100) + "%", 2000, true, "waring", false)
    }

    dxMap.get("VGPRO").put("updateTimeout", new Date().getTime())
}

//Audio or image update - End command
vgProService.cmd83 = function (data) {
    log.info("{vgPro} [cmd83] req:" + JSON.stringify(data))
    let pack = data.pack
    let md5 = pack.data.substring(4)
    let map = dxMap.get("VGPRO")
    let upgradeInfo = map.get('upgradeInfo')
    //3. Calculate and compare whether the MD5 hashes are the same.
    let firmware = ota.OTA_ROOT + '/download.zip'
    const temp = ota.OTA_ROOT + '/temp'

    let size = parseInt(common.systemWithRes(`file="${firmware}"; [ -e "$file" ] && wc -c "$file" | awk '{print $1}' || echo "0"`, 100).split(/\s/g)[0])
    if (size != upgradeInfo.size) {
        map.del('upgradeInfo')
        send(data.source, { "cmd": '83', "result": '90', "length": 0, 'data': '' })
        return
    }
    let md5Hash = common.md5HashFile(firmware)
    md5Hash = md5Hash.map(v => v.toString(16).padStart(2, 0)).join('')
    if (md5Hash != md5) {
        map.del('upgradeInfo')
        send(data.source, { "cmd": '83', "result": '90', "length": 0, 'data': '' })
        return
    }
    //Unzip
    common.systemBrief(`mkdir -p ${temp} && unzip -q ${firmware} -d ${temp}`)
    //5. Build the script file
    let shell = codeService.updateResourceShell
    common.systemBrief(`echo "${shell}" > ${ota.OTA_RUN} && chmod +x ${ota.OTA_RUN}`)
    let res = common.systemWithRes(`test -e "${ota.OTA_RUN}" && echo "OK" || echo "NO"`, 2)
    if (!res.includes('OK')) {
        throw new Error('Build shell file failed')
    }
    common.systemWithRes(`${ota.OTA_RUN}`)
    map.del('upgradeInfo')
    send(data.source, { "cmd": '83', "result": '00', "length": 0, 'data': '' })

    dxMap.get("VGPRO").del("updateTimeout")

    if (config.get("sysInfo.language") == 1) {
        driver.screen.customPopWin("Upgrade Successfully", 2000, true, "waring", false)
    } else {
        driver.screen.customPopWin("Upgrade successful", 2000, true, "waring", false)
    }
}

// Audio or image updates; deleting audio files or images.
vgProService.cmd8a = function (data) {
    log.info("{vgPro} [cmd8a] req:" + JSON.stringify(data))
    let pack = data.pack
    let type = pack.data.substring(0, 2)
    let index = parseInt(pack.data.substring(2), 16)
    if (type != '01' && type != '02') {
        send(data.source, { "cmd": '8A', "result": '90', "length": 0, 'data': '' })
        return
    }
    if (type == '01') {
        //Delete image
        let res = common.systemWithRes(`test -e "/app/code/resource/image/${index}.png" && echo "OK" || echo "NO"`, 2)
        if (!res.includes('OK')) {
            send(data.source, { "cmd": '8A', "result": '90', "length": 0, 'data': '' })
            return
        }
        common.systemBrief('rm -rf /app/code/resource/image/' + index + '.png')
    } else if (type == '02') {
        let res = common.systemWithRes(`test -e "/app/code/resource/wav/${index}.wav" && echo "OK" || echo "NO"`, 2)
        if (!res.includes('OK')) {
            send(data.source, { "cmd": '8A', "result": '90', "length": 0, 'data': '' })
            return
        }
        //Delete audio
        common.systemBrief('rm -rf /app/code/resource/wav/' + index + '.wav')
    }
    send(data.source, { "cmd": '8A', "result": '00', "length": 0, 'data': '' })
}


function send(source, data) {
    switch (source) {
        case '485':
            driver.uart485.sendVg(data)
            break;
        case 'tcp':
            driver.tcp.sendVg(data)
            break;
        // case 'usb':
        //     driver.uartUsb.sendVg(data)
        //     break;
        default:
            break;
    }
}

/**
 * Parse the string as json
 * @param {*} string 
 * @returns 
 */
function parseString(string) {
    const configMatch = string.match(/\{(.+?)\}/);
    if (configMatch && configMatch.length >= 2) {
        const configData = configMatch[1];
        const keyValuePairs = configData.split(',').map(pair => {
            const [key, value] = pair.split('=');
            const trimmedValue = value.trim();
            const parsedValue = trimmedValue.startsWith('"') && trimmedValue.endsWith('"')
                ? trimmedValue.slice(1, -1) // Remove double quotes and parse into a string.
                : Number(trimmedValue);     // Parsing into numerical values
            return [key.trim(), parsedValue];
        });
        const jsonObject = Object.fromEntries(keyValuePairs);
        return jsonObject;
    }
    return null;
}

//Verify configuration code
function checkConfigCode(code) {
    let password = config.get('sysInfo.com_passwd') || '1234567887654321'
    let lastIndex = code.lastIndexOf("--");
    let firstPart = code.substring(0, lastIndex);
    let secondPart = code.substring(lastIndex + 2);
    let res
    try {
        res = base64.fromHexString(common.arrayBufferToHexString(common.hmac(firstPart, password)))
    } catch (error) {
        log.error(error, error.stack)
        return false
    }
    return res == secondPart;
}

export default vgProService